/**
 * CImage.java
 *
 * The CImage class encapsulates an image. It not only
 * stores the image within itself, but also stores more
 * information like the relative positioning of the image
 * and the collision rectangle associated with the image.
 *
 * class Dependencies: None
 */

package gamepack;

import java.awt.Rectangle;
import java.awt.Image;
import java.awt.Polygon;
import java.awt.Shape;
/**
 *  CImage could have extended Image but it didn't.
 *  Instead of implementing 4 abstract methods of Image, containing
 *  Image object as a member makes feels simpler, and less dependent on Image
 *
 */
public class CImage implements Collidable { 

	/** The image object 
	*/	
	public Image img;

	/** 
		This is the anchor/center of the image with the sign inversed.
		
		For Eg. If Image is a square of size 100x100 pixel, and its center needs to defined at 50,50 then 
		x should be defined as -50 and y should be defined as -50 since the convention requires the sign to be inversed.

		By default, if the center of a Image is not defined then, the center of the image is 
		usually (-img.getWidth/2 and -img.getHeight/2. 
		The anchor/center here means if one wants to rotate the image, then it should rotate it around this point. 
		Sometimes the rotation point may not be same as the center of the image.
		
		Center of 4x4 pixel image = -2,-2  ( -Image.getWidth/2 and -Image.getHeight/2 )   
		Center of 5x5 pixel image = -2,-2  ( -Image.getWidth/2 and -Image.getHeight/2 )
		Center of 3x3 pixel image = -1,-1  ( -Image.getWidth/2 and -Image.getHeight/2 )
		
	                        [ ][ ][ ][ ]          [ ][ ][ ][ ][ ]
		[ ][ ][ ]        	[ ][ ][ ][ ]          [ ][ ][ ][ ][ ]   
		[ ][0][ ][ ]     	[ ][ ][0][ ][ ]       [ ][ ][0][ ][ ][ ]
		[ ][ ][ ]        	[ ][ ][ ][ ]          [ ][ ][ ][ ][ ]   
		   [ ]        	          [ ]             [ ][ ][ ][ ][ ]      
	                                                    [ ]	                                       
											   (If both even/odd sized image with width/height as 3,4,5 at blited at same point.
											    between 3 and 4, the image 4 will be more on top and left.
	                                            between 4 and 5, the image 5 will be more on the right and bottom.
     */												
	public int x=0,y=0;

	/** This defines the bounding collision rectangle that should be used for collision detection.
		The bounding rectangle is defined relative to the center of the image (CImage.x,CImage.y)
		
		Eg Say the below Image of size 5x5 represents a Tank (top view) whose nozzle is facing left. 
		Say 'c' is the center of the Tank and we do not want the nozzle to be used for collision testing.

		Here the rect should be defined as 
		rect.x = -1		rect.y = -2     
		rect.width = 3  rect.height = 4
		
		[ ][ ][x][x][x]               +-------+
		[ ][ ][x][x][x]               |       |
		[x][x][x][c][x]       +-------+   c   |
		[ ][ ][x][x][x]               |       |
		[ ][ ][x][x][x]		 	      +-------+
		
		Note that rect.x and rect.y are defined with respect to anchor/center c at (4,2). 	
		The bounding collision rectangle will be used for collision testing. If there is a collision and if hasSubShape() is true
		then the getSubShape() method is used for collision detection.
		CImage.getBounds() returns this value;		
	*//*		
	*/
	private Rectangle rect;
	
	/** This defines the polygon that will be used for collision detection. The polygon is defined relative to the anchor/center of the image.
	
		Eg. Eg Say the below Image of size 5x4 represents a Tank (top view) whose nozzle is facing left. 
		
		[ ][ ][x][x][x]          +-------+
		[x][x][x][c][x]     +----+   c   |
		[x][x][x][x][x]     +----+       |
		[ ][ ][x][x][x]		     +-------+
		
		Say in the above image polygon would be defined relative to its anchor/center 'c' at (4,2)
		The polygon coordinate would be (-1,-1) ( 1,-1) ( 1,2) (-1, 2) (-1,-1) (-3, 1) (-3, 0) (-1, 0)
	
		This may be null, indicating that there is no
		polygon associated and only 'rect' should be used for collection testing. If this is null, then hasSubShape() returns false	
		getSubShape() returns this polygon.
		If this is not null then CImage.getSubShape().getBounds() should be same as CImage.getBounds()
	*//*
		(Unlike default 'rect', There is no such thing as a default polygon, Instead polygon is set to null. 
		However if we were to create a polygon which mimics the default 'rect' behaviour then it would be - 

		The default polygon for a image which should be the bounding rectangle for the image would be.
		One way would be define the polygon wrt to (0,0) and then translate it to the center (cx,cy)
		
		Polygon p (wrt 0,0)          => ( 2, 0) ( 4, 0) ( 4, 3)
		p.translate(-4,-2) (wrt x,y) => (-2,-2)
		------------------------------------------------
	*/
	public Polygon polygon; 

	/** 
		Minimal CImage constructor that takes only Image as the argument.
		By default the anchor point of image is centered within the image.
		ie. x =  - img.getWidth()/2;  (The value is negative as per convention see @x) 
			y =  - img.getHeight()/2
		By default the whole image is considered as the collision rectangle.
			rect.x = - img.getWidth()/2;		
			rect.y = - img.getHeight()/2;		
			rect.width = img.getWidth();
			rect.height= img.getHeight();
	*/
	public CImage(Image img) {
		this(img,-(img.getWidth(null)/2),-(img.getHeight(null)/2),null);
	}

    /**
		Consturcts a CImage object.
		@Image - The underlying Image object @see CImage.img
		@x,y - The anchor/center point of the Image		
		@rect - The collision rectangle of the Image. If it is null then by default
				it is assumed to be Rectangle(x,y,width,height). The default rectangle calculated 
				will not be appropriate if the anchor x,y of the image is not at the center of the image.
				
				NOTE - 
				**** The collision rectangle should be defined relative to the center of the image (x,y). See @rect ***
	    */
	 
	public CImage(Image img,int x,int y,Rectangle rect) {
		set( img,x,y,rect);
	}
	
	public void setRect( Rectangle r ) {
		this.rect = new Rectangle( r ) ;
		polygon = null;
	}
	
	public void setPolygon(Polygon p) {
		this.polygon = new Polygon( p.xpoints, p.ypoints, p.npoints );		
		this.rect = polygon.getBounds();
		rect.width++;
		rect.height++;		
	}
	
	private void set(Image img,int x,int y,Rectangle rect) {
		if(img == null) throw new GamePackException
		     			("CImage constructor called with Invalid parameter");
		this.img = img;
		this.x = x;		
		this.y = y;
		
        int width = img.getWidth(null);
        int height = img.getHeight(null);

		if( rect == null ) {
			// Note we are defining the rectangle relative to the (x,y) which is the center of the image.			
			// If x,y was not positioned at the center of image then this collision rect will not get created properly.
			this.rect = new Rectangle( x , y , width,height);			
		}
		else {	
			// It is assumed that the rectangle is defined relative to the center of the image 
			// and not with respect to top left corner of the image.
			this.rect = new Rectangle( rect );	
		}
		polygon = null; 	// lets be explicit, though strictly not required.						
	}
	
	// Now the trick is how does the ResourceLoader aware of the polygon
	/**
		Consturcts a CImage object.
		@Image - The underlying Image object @see CImage.img
		@x,y - The anchor/center point of the Image		
		@polygon - The collision polygon of the Image. If it is null hasSubShape() returns false;
				   if not null, then 
				   getSubShape() returns this polygon and 
				   Cimage.getBounds() returns polygon.getBounds();				   
				   
				   A difference between Rectangle and Polygon,
				   While storing a rectangle, we store the width and height the actuall collsion rectangle is width-1 and height -1
				   but in a polygon, we don't store the width and height anywhere, the polygon co-ordinates itself use width-1 and height-1

				   The problem is that polygon.getBounds() returns a rectangle with 1 less width and 1 less height
				
		NOTE - Care must be takes to specify the co-ordinates of the polygon relative to center of the image (x,y). See @polygon 
		This can be a common reason for incorrect collisions
		@dummyObj - dummy parameter can be ignored. 
		***		
	*/
	public CImage(Image img,int x,int y,Polygon p,Object dummyObj) {
		// We need to add 1 pixel to the width and height of the polygon's bounding rectangle because the rectangle's width		
		Rectangle rb = p.getBounds();
		rb.width ++;
		rb.height ++;
		set( img,x,y,rb );		//this( img, x,y, p.getBounds() );				
		polygon = p;  
	}

	public Rectangle getBounds() {
		return rect;
	}
	
	public void translate(int dx,int dy) {
		rect.translate(dx,dy);
		if( polygon != null ) {
			polygon.translate(dx,dy);
		}
	}
	   
	public boolean hasSubShape() {
		return polygon == null ? false : true;
	}
   
   public Polygon getSubShape() {
		return polygon;
   }
	
	

}
/*

                   +--> Say Center of Img (15,15)
                   |
         (0,0)     |      (30,0)
           +-------|--------+
           |(12,10)|        |  
           |     +----+     +----+ (40,10)
           |     | +  |          | 
           |     +----+     +----+ (40,20)
           |        |(20,20)|
           +--------|-------+(30,30)
                    |
					+--> Say Coll.Rectangle(12,10, 8,10) 
					
			Say Width of Image = 40
			Say Height of Image = 30
			Say Anchor point of Image is at (15,15)
			Say Inner small box at (12,10) is the collision rectangle whose width,height is 8,10. 

Then CImage will be defined as 
{
	x = -15;  // coz to blit img at px,py, we will be using (px+x,py+y) as blit location.
	y = -15; 
	r = Rectangle(-3,-5,8,10); // coz if sprite loc is (px,py) then Collision Rectangle (xR,yR,wR,hR) is ( px+r.x,py+r.y, width, height)	
}					
Eg. 
If Sprite positioned at (px,py) = (0,0) 
then Blit location will be (px+x,py+y) = (-15,-15)
and Collision Rect will be ( px+r.x  , py+r.y, r.width, r.height) = (-3,-5, 8, 10)

If x and y are not specified they default to center of image in this case it will be (-20,15) instead of (15,15)
If collision rect is not specified defaults are used which is r.x = x and r.y = y;
 as per defaults the collision rect for the sprite will be (-15,-15,40,30)

Sample values used for Tank sprites.
T1_LEFT.: -22:-13: -13:-13:26:26 < Dimen 35x26    
T1_RIGHT.: -13:-13:  -13:-13:26:26 < Dimen 35x26
T1_UP. -13:-22: -13:-13:26:26 </  Dimen 26x35
T1_DOWN: -13:-13: -13:-13:26:26 </ Dimen 35x26

*Important thing to note is that the center of the image defined is wrt to top right corner of the image but the collision rectangle
is defined relative to the center of the image and this is more natural way of representing it programatically.*
"For the artist, it may be easier to define the collision rectangle with respect to top left corner of the image, so change can be made
at the ResourceLoader level to read values from xml file in the way suitable for artist, but internally CImage will maintain it in the
above convention.
------------------------------------------------- Verify the below Gyan
Now let us assume, the above Image was supposed to be stored as a 4 sided polygon (same as the original image size) with center at 15,15 and
the sprite is supposed to be displayed at location (0,0) on screen then - 
x = -15, y = -15 // CImage's center 
getSubShape() will return a Polygon p whose co-orinates are - 

// Lets go clockwise starting from top right corner 
If the polygon is defined by xn,yn then the collision polygon is defined by pxn,pyn
x1 y1 0,0    px1,py1 = (x+x1 , y+y1) = (-15+ 0,-15+ 0) = (-15,-15)
x2 y2 40,0   px2,py2 = (x+x2 , y+y2) = (-15+40,-15+ 0) = ( 25,-15)
x3 y3 40,30  px3,py3 = (x+x3 , y+y3) = (-15+40,-15+30) = ( 25, 15)
x4 y4 0,30   px4,py4 = (x+x4 , y+y4) = (-15+ 0,-15+30) = (-15, 15)

-----------------------------------------------------
General Gyan - 
Both CImage and Sprite implement Collidable interface, the difference is that
	The bounding rect/collision polygon returned by CImage is wrt to center of the Image defined by -> CImage.x and CImage.y
	The bounding rect/collision polygon returned by Sprite is its current CImage translated by the Sprite's location defined by Sprite.x and Sprite.y The current CImage can be obtained by call to Sprite.getCImage();
		
If hasSubShape() is true, then both CImage and Sprite should return polygon.getBounds() which should match with CImage.rect
this implies CImage.rect should be intialized to polygon.getBounds() during construction.




*/
